package com.tianji.learning.service.impl;

import java.time.LocalDateTime;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.dto.IdAndNumDTO;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.enums.LessonStatus;

import cn.hutool.core.collection.CollUtil;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.trade.OrderBasicDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课表 服务实现类
 * </p>
 *
 * @author Ys
 * @since 2023-08-06
 */
@Service
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    @Autowired
    private CourseClient courseClient;

    // 将用户购买的课程表里保存成课表
    // 注意:在设计表的时候使用了用户id和课程id做联合约束，因此不存在重复购买问题(幂等性解决了)
    // 添加课程到课表
    @Override
    public void saveLesson(OrderBasicDTO orderBasicDTO) {
        //1. 根绝课程id和远程调用tj-course查询课程信息
        List<CourseSimpleInfoDTO> courseList = courseClient.getSimpleInfoList(orderBasicDTO.getCourseIds());
        if (CollUtil.isEmpty(courseList)) {
            log.error("课程不存在，无法生成课表");
            return;//结束流程
        }
        //2. 遍历课程列表，保存成学院课表
        ArrayList<LearningLesson> lessonList = new ArrayList<>(courseList.size());
        for (CourseSimpleInfoDTO course : courseList) {
            //2-1 创建课表对象
            LearningLesson learningLesson = new LearningLesson();
            //2-2 开始赋值
            learningLesson.setUserId(orderBasicDTO.getUserId());//学员id
            learningLesson.setCourseId(course.getId());//课程id

            //过期时间=当前时间+课程有效期
            Integer validDuration = course.getValidDuration();
            if (validDuration != null && validDuration > 0) {
                learningLesson.setExpireTime(LocalDateTime.now().plusMonths(validDuration));
            }
            //2-3 设置到列表list
            lessonList.add(learningLesson);
        }
        //3. 批量保存
        this.saveBatch(lessonList);
    }

    //1. 分页查询我的课表信息
    //2. 判断当前用户是否有课表数据
    //3. 根据课表id批量查询课程信息，转为Map集合
    //4. 遍历课表封装vo
    //5. 返回结果
    //分页查询我的课表
    @Override
    public PageDTO<LearningLessonVO> findByPage(PageQuery pageQuery) {
        //1. 分页查询我的课表信息
        //1-1 分页
        Page<LearningLesson> page = new Page<>(pageQuery.getPageNo(), pageQuery.getPageSize());
        //1-2 条件查询
        this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .page(page);
        //2. 判断当前用户是否有课表数据
        List<LearningLesson> learningLessonList = page.getRecords();
        if (CollUtil.isEmpty(learningLessonList)) {
            return PageDTO.empty(page); //无课表，返回空对象
        }
        //3. 根据课程id批量查询课程信息，转为Map集合
        Map<Long, CourseSimpleInfoDTO> courseMap = findCourseMapByCourseIds(learningLessonList);
        //4. 遍历课表封装vo
        ArrayList<LearningLessonVO> voList = new ArrayList<>(learningLessonList.size());
        for (LearningLesson learningLesson : learningLessonList) {
            //4-1 从learningLesson中赋值
            LearningLessonVO learningLessonVO = BeanUtil.copyProperties(learningLesson, LearningLessonVO.class);
            //4-2 获取课程信息，填充到vo
            CourseSimpleInfoDTO course = courseMap.get(learningLesson.getCourseId());
            learningLessonVO.setCourseName(course.getName()); //课程名
            learningLessonVO.setCourseCoverUrl(course.getCoverUrl());//课程封面
            learningLessonVO.setSections(course.getSectionNum());//课程总小节数
            //4-3收集
            voList.add(learningLessonVO);
        }
        // 5. 返回结果集
        return PageDTO.of(page,voList);
    }

    @Autowired
    private CatalogueClient catalogueClient;

    //1. 查询最新学习的课表
    //2. 根据课表中的课程id调用课程微服务查询课程信息
    //3. 根据课表中的章节id调用课程微服务查询章节信息
    //4. 封装VO
    //5. 返回结果
    //查询最近学习的一个课程
    @Override
    public LearningLessonVO findNowLesson() {
        //1. 查询最新学习的课表
        // select * from learning_lesson where user_id = #{userId} AND status = 1 order by latest_learn_time limit 1
        LearningLesson learningLesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if(ObjectUtil.isEmpty(learningLesson)){
            return null;
        }
        //2. 根据课表中的课程id调用课程微服务查询课程信息
        CourseFullInfoDTO courseFullInfoDTO = courseClient.getCourseInfoById(learningLesson.getCourseId(),
                false, false);
        if(ObjectUtil.isEmpty(courseFullInfoDTO)){
            return null;
        }
        //3. 根据课表中的章节id调用课程微服务查询章节信息
        List<CataSimpleInfoDTO> cataSimpleInfoDTOList = catalogueClient.batchQueryCatalogue(CollUtil.newArrayList(learningLesson.getLatestSectionId()));
        if(CollUtil.isEmpty(cataSimpleInfoDTOList)){
            return null;
        }
        CataSimpleInfoDTO cataSimpleInfoDTO = cataSimpleInfoDTOList.get(0);
        //4. 封装VO
        //4-1 拷贝PO基础属性到VO
        LearningLessonVO learningLessonVO = BeanUtil.copyProperties(learningLesson, LearningLessonVO.class);
        //4-2 课程信息
        learningLessonVO.setCourseName(courseFullInfoDTO.getName());//课程名称
        learningLessonVO.setCourseCoverUrl(courseFullInfoDTO.getCoverUrl());//封面
        learningLessonVO.setSections(courseFullInfoDTO.getSectionNum());//总章节数
        //4-3 章节信息
        learningLessonVO.setLatestSectionName(cataSimpleInfoDTO.getName());//章节名称
        learningLessonVO.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());//章节序号
        //4-4 课程数量
        //select count(*) from learning_lesson where user_id = #{userId}
        Integer courseAmount = this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .count();
        learningLessonVO.setCourseAmount(courseAmount);//课程数量
        //5. 返回结果
        return learningLessonVO;
    }

    //1. 根据用户id和课程id查询课表
    //2.根据课程id查询课程信息
    //3.封装vo
    //4.返回结果
    @Override
    public LearningLessonVO findLessonStatus(Long courseId) {
        //1. 根据用户id和课程id查询课表
        LearningLesson learningLesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if(ObjectUtil.isEmpty(learningLesson)){
            throw new BadRequestException("课表不存在");
        }
        //2. 根据课程id查询课表信息
        CourseFullInfoDTO courseFullInfoDTO = courseClient.getCourseInfoById(learningLesson.getCourseId(), false, false);
        if(ObjectUtil.isEmpty(courseFullInfoDTO)){
            throw new BadRequestException("课程不存在");
        }
        //3. 封装vo
        LearningLessonVO learningLessonVO = BeanUtil.copyProperties(learningLesson, LearningLessonVO.class);
        learningLessonVO.setCourseName(courseFullInfoDTO.getName());
        learningLessonVO.setCourseCoverUrl(courseFullInfoDTO.getCoverUrl());
        learningLessonVO.setSections(courseFullInfoDTO.getSectionNum());
        //4.返回结果
        return learningLessonVO;
    }

    //1. 根绝用户id和课程id查询课表
    //2. 判断课程状态是否有效
    //3. 返回课表id
    // 校验当前用户是否拥有当前课程
    @Override
    public Long validLesson(Long courseId) {
        //1. 根据用户id和课程id查询课表
        LearningLesson learningLesson = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .one();
        if(ObjectUtil.isEmpty(learningLesson)){
            log.error("课表不存在");
            return null;
        }
        //2. 判断课程状态是否有效
        if(learningLesson.getStatus().getValue() == LessonStatus.EXPIRED.getValue()){
            log.error("当前课程状态已过期");
            return null;
        }
        //3. 返回课表id
        return learningLesson.getId();
    }

    //统计指定课程学习人数
    @Override
    public Integer countLesson(Long courseId) {
        return this.lambdaQuery()
                .eq(LearningLesson::getCourseId,courseId)
                .count();
    }

    //1. 查询我的课表
    //2. 修改计划
    @Override
    public void createLearningPlan(LearningPlanDTO planDTO) {
        //1. 查询我的课表
        LearningLesson learningLesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getCourseId, planDTO.getCourseId())
                .one();
        if(learningLesson==null){
            throw new BizIllegalException("课表不存在");
        }
        //2. 修改计划
        learningLesson.setWeekFreq(planDTO.getFreq());
        learningLesson.setPlanStatus(PlanStatus.PLAN_RUNNING);
        this.updateById(learningLesson);
    }

    //0. 创建返回结果对象
    //1. 获取本周时间区间
    //2. 查询本周总体情况(所有课程)
    //3. 分页查询我的课程列表
    //4. 如果没有课表，返回空对象
    //5. 查询课表对应的课程
    //6. 统计我的课程本周已学习小结
    //7. 遍历课表，填充LearningPlanVO
    //8. 返回结果

    @Autowired
    private LearningRecordMapper learningRecordMapper;

    //查询学习计划
    @Override
    public LearningPlanPageVO findLearningPlans(PageQuery pageQuery) {
        //0. 创建返回结果对象
        LearningPlanPageVO learningPlanPageVO = new LearningPlanPageVO();
        //1. 获取本周时间区间
        Date now = new Date();
        DateTime beginOfWeek = DateUtil.beginOfWeek(now);
        DateTime endOfWeek = DateUtil.endOfWeek(now);
        //2. 查询本周总体情况 (所有课程)
        //2-1 统计本周学习小节数(课表：当前用户，有计划，学习状态:未学习/学习中)
        // select sum(week_freq) from learning_lesson where user_id =#{userId} and plan_status =1 and status in (0,1)
        Integer weekTotalPlan= getBaseMapper().getWeekTotalPlan(UserContext.getUser());
        learningPlanPageVO.setWeekTotalPlan(weekTotalPlan);
        //2-2 统计本周已学习小结数(记录:当前用户，已完成，本周范围)
        //select count(*) from learning_record where user_id = #{userId} and finished =true and finish_time
        //between 周始 and 周末
        LambdaQueryWrapper<LearningRecord> qw = new LambdaQueryWrapper<>();
        qw.eq(LearningRecord::getUserId,UserContext.getUser())
                .eq(LearningRecord::getFinished,true)
                .between(LearningRecord::getFinishTime,beginOfWeek,endOfWeek);
        Integer weekFinished = learningRecordMapper.selectCount(qw);
        learningPlanPageVO.setWeekFinished(weekFinished);
        //2-3 本周积分奖励
        //todo 先写死
        learningPlanPageVO.setWeekPoints(100);
        //3. 分页查询我的课程列表
        //3-1 设置分页
        Page<LearningLesson> page = new Page<>(pageQuery.getPageNo(), pageQuery.getPageSize());
        //3-2 条件查询 (当前用户、正在计划中、学习状态:未学习/学习中)
        this.lambdaQuery()
                .eq(LearningLesson::getUserId,UserContext.getUser())
                .eq(LearningLesson::getPlanStatus,PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus,LessonStatus.NOT_BEGIN.getValue(),LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .page(page);
        //4.如果没有课表，返回空对象
        List<LearningLesson> learningLessonList = page.getRecords();
        if(CollUtil.isEmpty(learningLessonList)){
            learningPlanPageVO.setTotal(page.getTotal());
            learningPlanPageVO.setPages(page.getPages());
            learningPlanPageVO.setList(CollUtil.newArrayList());
            return learningPlanPageVO;
        }
        //5. 查询课表对应的课程
        Map<Long, CourseSimpleInfoDTO> courseMap = findCourseMapByCourseIds(learningLessonList);
        //6. 统计我的课程本周已学习小节
        //select lesson_id,count(*) from learning_record where user_id = #{userId} and finished = true
        //and finish_time between #{beginTime} and #{endTime} group by lesson_id
        List<IdAndNumDTO> idAndNumDTOList = learningRecordMapper.countLesson(UserContext.getUser(), beginOfWeek, endOfWeek);
        Map<Long, Integer> lessonCountMap = IdAndNumDTO.toMap(idAndNumDTOList);
        //7. 遍历课表，填充LearningPlanVO
        ArrayList<LearningPlanVO> learningPlanVOList = new ArrayList<>(learningLessonList.size());
        for (LearningLesson learningLesson : learningLessonList) {
            //7-1 learningLesson->LearningPlanVO
            LearningPlanVO learningPlanVO = BeanUtil.copyProperties(learningLesson, LearningPlanVO.class);
            //7-2 设置课程名称和章节
            CourseSimpleInfoDTO courseSimpleInfoDTO = courseMap.get(learningLesson.getCourseId());
            learningPlanVO.setCourseName(courseSimpleInfoDTO.getName());
            learningPlanVO.setSections(courseSimpleInfoDTO.getSectionNum());
            //7-3 设置本周已学习的小节数
            learningPlanVO.setWeekLearnedSections(lessonCountMap.getOrDefault(learningLesson.getId(),0));
            //7-4添加到vo集合
            learningPlanVOList.add(learningPlanVO);
        }
        //8. 返回结果
        learningPlanPageVO.setTotal(page.getTotal());
        learningPlanPageVO.setPages(page.getPages());
        learningPlanPageVO.setList(learningPlanVOList);
        return learningPlanPageVO;
    }

    // 根据课程id批量查询课程信息，转为Map集合
    private Map<Long, CourseSimpleInfoDTO> findCourseMapByCourseIds(List<LearningLesson> learningLessonList) {
        // 获取课程id集合
        List<Long> courseIds = learningLessonList.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());
        // 调用tj-course查询课程
        List<CourseSimpleInfoDTO> courseList = courseClient.getSimpleInfoList(courseIds);
        if(CollUtil.isEmpty(courseList)){
            //课程不存在
            throw new BadRequestException("课程信息不存在");
        }
        // 转Map
        /*
        *  Map<Long,CourseSimpleInfoDTO> courseMap=new HashMap();
        * for (CourseSimpleInfoDTO course: courseList) {
        *   courseMap.put(course.getId(),course);
        * }
        * */
        Map<Long, CourseSimpleInfoDTO> courseMap = courseList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        return courseMap;
    }
}
